<script setup lang="ts">
import {
  type ComponentInternalInstance,
  type ComponentPublicInstance,
  computed,
  inject,
  provide,
  reactive,
  ref,
  useAttrs
} from 'vue';
import { NButton, NModal } from 'naive-ui';
import { type Size } from 'naive-ui/es/button/src/interface';
import { type Type } from 'naive-ui/lib/button/src/interface';
import type { DynamicComponent, DynamicComponentEmitArgs } from '@/typings/por.components';
import { alova } from '@/service/request';

defineOptions({
  name: 'PorButton',
  inheritAttrs: false
});

const props = withDefaults(
  defineProps<{
    confirmText?: string;
    url?: string;
    icon?: string;
    tipText?: string;
    size?: Size;
    /** 如果点击后显示弹窗，这里传入弹窗组件 */
    modalComponent?: ComponentPublicInstance;
    /** 弹窗组件要显示的html */
    modalHtml?: string;
    /** 弹窗组件需要接收的参数 */
    modalComponentProps?: Record<string, any>;
    /** 编辑弹窗宽度 */
    modalWidth?: string;
    /** 弹窗标题 */
    modalTitle?: string;
    /** 弹窗参数，参考naive-ui的modal组件 */
    modalArgs?: Record<string, any>;
    /** 如果点击后显示抽屉，这里传入弹窗组件 */
    drawerComponent?: ComponentPublicInstance;
    /** 抽屉组件要显示的html */
    drawerHtml?: string;
    /** 抽屉标题 */
    drawerTitle?: string;
    /** 抽屉参数，参考naive-ui的modal组件 */
    drawerArgs?: Record<string, any>;
    /** 抽屉组件需要接收的参数 */
    drawerComponentProps?: Record<string, any>;
    /** 抽屉的确认按钮文字，为空则不显示该按钮 */
    drawerPositiveText?: string;
    /** 抽屉的确认按钮样式，参考n-button的type，为空则不显示该按钮 */
    drawerPositiveType?: Type;
    /** 抽屉的取消按钮文字，为空则不显示该按钮 */
    drawerNegativeText?: string;
    /** 抽屉的确认按钮样式，参考n-button的type，为空则不显示该按钮 */
    drawerNegativeType?: Type;
    /** 是否是一个超链接按钮 */
    isLinkBtn?: boolean;
    /** 是否是一个下载按钮,如果传入字符串，则会用该字符串作为文件名下载，如果传入true，则会用连接地址的默认文件名保存 */
    download?: string | true;
    /** 下载请求附带的参数 */
    downloadForm?: Record<string, any>;
    /** 是否是一个上传按钮 */
    upload?: boolean;
    /** 上传的字段名，默认为file */
    uploadFieldName?: string;
    /** 上传接受的文件类型 */
    uploadAccept?: string;
    /** 上传请求附带的参数 */
    uploadForm?: Record<string, any>;
  }>(),
  {
    modalWidth: '800px',
    modalComponentProps: () => ({}),
    modalArgs: () => ({}),
    drawerPositiveText: '确认',
    drawerPositiveType: 'primary',
    drawerNegativeText: '关闭',
    drawerNegativeType: 'default',
    isLinkBtn: false,
    uploadFieldName: 'file'
  }
);
const attrs: {
  onClick?: () => void;
  [key: string]: any;
} = useAttrs();
const emit = defineEmits<{
  (e: 'confirm'): void;
  (e: 'cancel'): void;
  /** 请求成功后的回调 */
  (e: 'success', result: Api.ResData): void;
  /** 请求失败后的回调 */
  (e: 'error', result: Api.ResData): void;
  /** Modal弹出框 点击确认按钮后的回调 */
  (e: 'modalYesClick', value: DynamicComponentEmitArgs): void;
  /** Modal弹出框 点击取消按钮后的回调 */
  (e: 'modalNoClick', value: DynamicComponentEmitArgs): void;
  /** Drawer抽屉 点击确认按钮后的回调 */
  (e: 'drawerYesClick', value: DynamicComponentEmitArgs): void;
  /** Drawer抽屉 点击取消按钮后的回调 */
  (e: 'drawerNoClick', value: DynamicComponentEmitArgs): void;
  /** Drawer抽屉 点击遮罩后的回调 */
  (e: 'drawerMaskClick', value: DynamicComponentEmitArgs): void;
  /** 上传成功后的回调 */
  (e: 'uploadSuccess', value: any): void;
  /** 上传失败后的回调 */
  (e: 'uploadFail', value: any): void;
  /** 上传后的回调 */
  (e: 'uploadComplete', value: any): void;
}>();

const state = reactive({
  loading: false,
  modalShown: false,
  drawerShown: false
});

const currentTable = inject<ComponentInternalInstance | null>('currentTable', null);

const size = computed<Size>(() => {
  // table内的button 默认 small size
  return (props.size ?? currentTable) ? 'small' : 'medium';
});

/** 弹窗相关 start */
const modalRef = ref<InstanceType<typeof NModal> | null>(null);
const modalComponentRef = ref<DynamicComponent | null>(null);

const modalArgs = computed(() => {
  return {
    positiveText: '确认',
    negativeText: '取消',
    maskClosable: false,
    ...props.modalArgs,
    title: props.modalTitle,
    style: { ...props.modalArgs?.style, width: props.modalWidth }
  };
});

function showModal() {
  state.modalShown = true;
}

function handleModalYesClick() {
  try {
    emit('modalYesClick', {
      close: closeModal,
      componentRef: modalComponentRef.value!,
      componentProps: props.modalComponentProps
    });
  } catch (error) {
    console.error(error);
  }

  return false;
}

function handleModalNoClick(): boolean {
  try {
    emit('modalNoClick', {
      close: closeModal,
      componentRef: modalComponentRef.value!,
      componentProps: props.modalComponentProps
    });
    return true;
  } catch (error: unknown) {
    console.error('Modal no click handler failed:', error);
    return false;
  }
}

function closeModal() {
  state.modalShown = false;
}

/** 弹窗相关 end */

/** 抽屉相关 start */
const drawerComponentRef = ref<DynamicComponent | null>(null);
const drawerArgs = computed(() => {
  return {
    defaultWidth: '800px',
    resizable: true,
    ...props.drawerArgs
  };
});

function showDrawer() {
  state.drawerShown = true;
}
function closeDrawer() {
  state.drawerShown = false;
}
function handleDrawerYesClick() {
  try {
    emit('drawerYesClick', {
      close: closeDrawer,
      componentRef: drawerComponentRef.value!,
      componentProps: props.drawerComponentProps
    });
  } catch (error) {
    console.error(error);
  }

  return false;
}
function handleDrawerNoClick(): boolean {
  try {
    emit('drawerNoClick', {
      close: closeDrawer,
      componentRef: drawerComponentRef.value!,
      componentProps: props.drawerComponentProps
    });
    closeDrawer();
    return true;
  } catch (error) {
    console.error(error);
    return false;
  }
}

function handleMaskClick(): boolean {
  try {
    emit('drawerMaskClick', {
      close: closeDrawer,
      componentRef: drawerComponentRef.value!,
      componentProps: props.drawerComponentProps
    });
    closeDrawer();
    return true;
  } catch {
    return false;
  }
}

/** 抽屉相关 end */

/** 点了确认 */
function doConfirm() {
  execute();
  emit('confirm');
}

/** 执行下载操作 */
function executeDownload() {
  // if (!props.url) return;
  // let fileName = '';
  // if (typeof props.download === 'string') {
  //   fileName = props.download;
  // }
  // alova.Get(props.url, fileName, props.downloadForm).onDownload();
}

const uploadFile = ref();
/**
 * 当上传的文件发生变动
 *
 * @param e
 */
function onUploadFileChange(e: any) {
  const files = e.target.files;
  if (!files || !files[0]) return;
  state.loading = true;
  alova
    .Post(
      props.url!,
      {
        [props.uploadFieldName]: files[0],
        ...props.uploadForm
      },
      {
        headers: {
          'Content-Type': 'multipart/form-data'
        }
      }
    )
    .then(res => {
      state.loading = false;
      e.target.value = null;
      interface UploadResponse {
        result: unknown;
        error: unknown;
      }
      emit('uploadSuccess', (res as UploadResponse).result);
      emit('uploadFail', (res as UploadResponse).error);
      emit('uploadComplete', res);
    });
}

/** 执行上传操作 */
function executeUpload() {
  if (!props.url) return;
  uploadFile.value.click();
}

async function execute() {
  if (props.url && props.isLinkBtn) {
    // 超链接按钮
    window.open(props.url);
  } else if (props.url && props.download) {
    executeDownload();
  } else if (props.url && props.upload) {
    executeUpload();
  } else if (props.url) {
    sendRequest();
  } else if (props.modalComponent || props.modalHtml) {
    showModal();
  } else if (props.drawerComponent || props.drawerHtml) {
    showDrawer();
  }
}

/** 确认点了取消 */
function doCancel() {
  emit('cancel');
}

/** 处理 点击事件 */
function handleClick() {
  event?.stopPropagation();
  if (attrs.onClick) {
    attrs.onClick();
    return;
  }
  // 如果是确认按钮，不执行点击事件
  if (props.confirmText) {
    return;
  }
  execute();
}

/** 根据url发送请求 */
async function sendRequest() {
  if (props.url) {
    if (state.loading) return;
    state.loading = true;
    const result = await alova.Get<Api.ResData>(props.url);
    state.loading = false;
    if (result.code !== '200') {
      emit('error', result);
      return;
    }
    emit('success', result);
  }
}

/** 将属性中的onclick过滤掉，因为如果有onclick，会覆盖掉我们的点击事件 */
const validAttrs = computed<Record<string, unknown>>(() => {
  const { onClick: _onClick, ...rest } = attrs;
  return rest;
});

defineExpose({
  closeModal,
  showDrawer,
  closeDrawer,
  showModal
});
provide('closeDrawer', closeDrawer);
provide('closeModal', closeModal);
</script>

<template>
  <div style="display: inline-flex" class="jb-btn">
    <!-- 既有提示信息，又有确认信息，提示信息显示在按钮上 -->
    <NTooltip v-if="props.tipText && props.confirmText" trigger="hover" placement="right">
      <template #trigger>
        <NPopconfirm @positive-click="doConfirm()" @negative-click="doCancel()">
          <template #trigger>
            <NButton :size="size" v-bind="validAttrs" :loading="state.loading" @click="handleClick">
              <template #default>
                <slot></slot>
              </template>
              <template #icon>
                <JbIcon v-if="props.icon" :icon="props.icon"></JbIcon>
              </template>
            </NButton>
          </template>
          {{ props.confirmText }}
        </NPopconfirm>
      </template>
      {{ props.tipText }}
    </NTooltip>
    <!-- 只有提示信息，提示信息显示在按钮上 -->
    <NTooltip v-else-if="props.tipText" trigger="hover">
      <template #trigger>
        <NButton v-bind="validAttrs" :size="size" :loading="state.loading" @click="handleClick">
          <template #default>
            <slot></slot>
          </template>
          <template #icon>
            <JbIcon v-if="props.icon" :icon="props.icon"></JbIcon>
          </template>
        </NButton>
      </template>
      {{ props.tipText }}
    </NTooltip>
    <!-- 只有确认信息 -->
    <NPopconfirm v-else-if="props.confirmText" @positive-click="doConfirm()" @negative-click="doCancel()">
      <template #trigger>
        <NButton v-bind="validAttrs" :size="size" :loading="state.loading" @click="handleClick">
          <template #default>
            <slot></slot>
          </template>
          <template #icon>
            <JbIcon v-if="props.icon" :icon="props.icon"></JbIcon>
          </template>
        </NButton>
      </template>
      {{ props.confirmText }}
    </NPopconfirm>
    <!--什么都没有 -->
    <NButton v-else v-bind="validAttrs" :size="size" :loading="state.loading" @click="handleClick">
      <template #default>
        <slot></slot>
      </template>
      <template #icon>
        <JbIcon v-if="props.icon" :icon="props.icon"></JbIcon>
      </template>
    </NButton>

    <NModal
      v-if="props.modalComponent || props.modalHtml"
      ref="modalRef"
      v-model:show="state.modalShown"
      preset="dialog"
      v-bind="modalArgs"
      @positive-click="handleModalYesClick"
      @negative-click="handleModalNoClick"
    >
      <component
        :is="props.modalComponent"
        v-if="props.modalComponent"
        ref="modalComponentRef"
        v-bind="props.modalComponentProps"
      ></component>
      <div v-else-if="props.modalHtml" class="html_wrapper" v-html="props.modalHtml"></div>
    </NModal>

    <NDrawer
      v-if="props.drawerComponent || props.drawerHtml"
      v-model:show="state.drawerShown"
      v-bind="drawerArgs"
      @mask-click="handleMaskClick"
    >
      <NDrawerContent :title="props.drawerTitle" :native-scrollbar="false" class="jb-btn_drawer">
        <component
          :is="props.drawerComponent"
          v-if="props.drawerComponent"
          ref="drawerComponentRef"
          v-bind="props.drawerComponentProps"
        ></component>
        <div v-else-if="props.drawerHtml" class="html_wrapper" v-html="props.drawerHtml"></div>
        <template #footer>
          <NSpace justify="end">
            <NButton v-if="props.drawerPositiveText" :type="props.drawerPositiveType" @click="handleDrawerYesClick()">
              {{ props.drawerPositiveText }}
            </NButton>
            <NButton v-if="props.drawerNegativeText" :type="props.drawerNegativeType" @click="handleDrawerNoClick()">
              {{ props.drawerNegativeText }}
            </NButton>
          </NSpace>
        </template>
      </NDrawerContent>
    </NDrawer>

    <input
      ref="uploadFile"
      type="file"
      style="display: none"
      :accept="props.uploadAccept"
      @change="onUploadFileChange($event)"
    />
  </div>
</template>

<style scoped>
.jb-btn_drawer :deep(.n-drawer-body-content-wrapper) {
  height: 100%;
}
</style>

<style>
.n-button-group:not(.n-button-group--vertical):not(.n-button-group--rtl)
  .jb-btn:first-child:not(:last-child)
  .n-button {
  margin-right: 0 !important;
  border-top-right-radius: 0 !important;
  border-bottom-right-radius: 0 !important;
}
.n-button-group:not(.n-button-group--vertical):not(.n-button-group--rtl)
  .jb-btn:not(:first-child):not(:last-child)
  .n-button {
  margin-left: 0 !important;
  margin-right: 0 !important;
  border-radius: 0 !important;
}
.n-button-group:not(.n-button-group--vertical):not(.n-button-group--rtl)
  .jb-btn:last-child:not(:first-child)
  .n-button {
  margin-left: 0 !important;
  border-top-left-radius: 0 !important;
  border-bottom-left-radius: 0 !important;
}
.n-button-group:not(.n-button-group--vertical):not(.n-button-group--rtl) .jb-btn:not(:first-child) {
  margin-left: -1px !important;
}
.html_wrapper img,
.html_wrapper video {
  max-width: 98%;
}
</style>
